package com.xes.attend.rd60n.jiecard.cn;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.concurrent.locks.ReentrantLock;

import struct.JavaStruct;

public class TCPServer extends Thread {
	private ServerSocket tcp_server = null;
	private TCPServerCallback callback = null;
	private ReentrantLock lock = null;
	private ArrayList<TCPClientThread> thread_list = null;

	public void StartServer(int listen_port, TCPServerCallback _callback) {
		try {
			tcp_server = new ServerSocket(listen_port, 100);
			callback = _callback;
			thread_list = new ArrayList<TCPClientThread>();
			lock = new ReentrantLock();
			start();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	void AddThread(TCPClientThread thread) {
		lock.lock();
		thread_list.add(thread);
		lock.unlock();

	}

	void RemoveThread(TCPClientThread thread) {
		lock.lock();
		thread_list.remove(thread);
		lock.unlock();
	}

	public void StopServer() {
		if (null != tcp_server) {
			try {
				tcp_server.close();
				tcp_server = null;
				this.join();

				lock.lock();
				for (int i = 0; i < thread_list.size(); ++i) {
					thread_list.get(i).Close();
				}
				lock.unlock();
				while (true) {
					lock.lock();
					if (thread_list.size() == 0) {
						lock.unlock();
						break;
					}
					lock.unlock();
					sleep(1);
				}

			} catch (Exception e) {
				tcp_server = null;
				e.printStackTrace();
			}
		}
	}

	@Override
	public void run() {
		Socket client;
		int err = 0;
		while (0 == err) {
			try {
				client = tcp_server.accept();
				client.setTcpNoDelay(true);
				TCPClientThread thread = new TCPClientThread(this, client,
						callback);
				thread.start();
			} catch (IOException e) {
				err = 1;
				e.printStackTrace();
			}
		}
	}
}

class TCPClientThread extends Thread {
	private TCPServer server = null;
	private Socket client = null;
	private TCPServerConn conn = null;
	private TCPServerCallback callback = null;

	public TCPClientThread(TCPServer s, Socket tcp_client,
			TCPServerCallback _callback) {
		server = s;
		client = tcp_client;
		callback = _callback;
		conn = new TCPServerConn(tcp_client);
	}
	private byte[] RecvData(int len) {

		byte[] recv_data = new byte[len];
		int result = 1;
		try {
			do {
				InputStream in = client.getInputStream();
				int l = 0;
				int tmp;
				while (len > 0) {

					tmp = in.read(recv_data, l, len);

					if (tmp <= 0) {
						break;
					}
					l += tmp;
					len -= tmp;
				}
				if (len != 0)
					break;

				result = 0;
			} while (false);
		} catch (Exception e) {
			Close();
			e.printStackTrace();
		}
		if (result == 0)
			return recv_data;
		else
			return null;
	}
	private CmdData ReadCmdData() {
		CmdData result = null;
		try {
			do {
				byte[] data = RecvData(8);
				if (null == data)
					break;
				if (data[0] != (byte) 0xAB)
					break;
				if (data[5] < 10)
					break;
				// if (data[6] != cmd_id)
				// break;
				byte[] data2 = RecvData(data[5] - 8);
				if (null == data2)
					break;
				byte crc = 0;
				for (int i = 0; i < data.length; ++i) {
					crc = (byte) (crc ^ data[i]);
				}
				for (int i = 0; i < data2.length - 1; ++i) {
					crc = (byte) (crc ^ data2[i]);
				}
				if (crc != data2[data2.length - 1])
					break;
				byte[] recv_data = new byte[data[5]];
				System.arraycopy(data, 0, recv_data, 0, 8);
				System.arraycopy(data2, 0, recv_data, 8, data2.length);
				recv_data[5] -= 9;
				result = new CmdData(null, (byte) 0);
				JavaStruct.unpack(result, recv_data, ByteOrder.LITTLE_ENDIAN);
			} while (false);
		} catch (Exception e) {
			e.printStackTrace();
			result = null;
		}
		return result;
	}

	public void run() {
		int err = 0;
		server.AddThread(this);
		while (0 == err) {
			try {
				CmdData cmd = ReadCmdData();
				if (null == cmd)
					err = 1;
				else
					callback.DeviceCallback(conn, cmd);
			} catch (Exception e) {
				e.printStackTrace();
				err = 1;
			}
		}

		try {
			client.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		server.RemoveThread(this);
	}

	public void Close() {
		if (null != client) {
			try {
				client.close();
				client = null;
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}
